package cn.virens.common.apifox.apifox;

import cn.virens.common.apifox.apifox.build.ApiFoxCollection;
import cn.virens.common.apifox.apifox.build.ApiFoxCollectionApi;
import cn.virens.common.apifox.apifox.build.ApiFoxCollectionParam;
import cn.virens.common.apifox.apifox.build.ApiFoxCollectionParamItem;
import cn.virens.common.apifox.apifox.util.ApifoxUtil;
import cn.virens.common.apifox.web.BaseController;
import cn.virens.common.util.exception.APIException;
import org.dromara.hutool.core.array.ArrayUtil;
import org.dromara.hutool.core.convert.ConvertUtil;
import org.dromara.hutool.core.io.file.FileUtil;
import org.dromara.hutool.core.reflect.ClassUtil;
import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ApifoxScanner {
    public String apifoxHeader;
    public String apifoxQuery;

    /**
     * 扫描指定包路径，返回接口列表
     */
    public List<ApiFoxCollection> scanPackage(String pkg, ApifoxTree tree) throws APIException {
        for (Class<?> clazz : ClassUtil.scanPackageBySuper(pkg, BaseController.class)) {
            RequestMapping mapping0 = clazz.getAnnotation(RequestMapping.class);
            ApifoxDoc postman0 = clazz.getAnnotation(ApifoxDoc.class);

            Arrays.stream(MethodUtil.getMethods(clazz)).forEach(method -> {
                RequestMapping mapping1 = method.getAnnotation(RequestMapping.class);
                ApifoxDoc postman1 = method.getAnnotation(ApifoxDoc.class);
                if (postman1 == null || mapping1 == null) return;

                // 根据类注解+方法注解获取对应的分组标识
                String group = normalize(group(postman0), group(postman1));

                // 生成接口文档
                parseDoc(mapping0, mapping1, postman1).forEach(c -> {
                    tree.addApi(group, fill(clazz, method, c));
                });
            });
        }

        return tree.toList();
    }

    public void setApifoxHeader(String apifoxHeader) {
        this.apifoxHeader = apifoxHeader;
    }

    public void setApifoxQuery(String apifoxQuery) {
        this.apifoxQuery = apifoxQuery;
    }

    public String getApifoxHeader() {
        return apifoxHeader;
    }

    public String getApifoxQuery() {
        return apifoxQuery;
    }

    /**
     * 填充注解(参数，请求体)
     */
    public ApiFoxCollection fill(Class<?> clazz, Method method, ApiFoxCollection api) throws APIException {
        api.getApi().setRequestBody(ApifoxUtil.request(clazz, method, api.getApi().getRequestBody()));
        api.getApi().setParameters(ApifoxUtil.param0(clazz, method, api.getApi().getParameters()));

        return api;
    }

    /**
     * 根据上级接口定义与当前的注解属性获取对应的接口申明实体
     */
    private Stream<ApiFoxCollection> parseDoc(RequestMapping parent, RequestMapping mapping, ApifoxDoc doc) {
        return Optional.of(path(parent)).map(paths -> Arrays.stream(paths).flatMap(path0 -> {
            return Optional.of(path(mapping)).map(p1s -> Arrays.stream(p1s).map(path1 -> {
                // 转化请求参数/请求头部分----------------------------------------------------------------
                ApiFoxCollectionParam param = new ApiFoxCollectionParam();
                param.setHeader(pasreHeader(doc.header()));
                param.setQuery(pasreQuery(doc.query()));

                // 转化接口路径/方式等接口信息-------------------------------------------------------------
                ApiFoxCollectionApi api = new ApiFoxCollectionApi();
                api.setDescription(doc.description());
                api.setPath(normalize(path0, path1));
                api.setMethod(method(mapping));
                api.setStatus("developing");
                api.setParameters(param);

                // 转化接口名称等基本信息-----------------------------------------------------------------
                ApiFoxCollection collection = new ApiFoxCollection();
                collection.setDescription(doc.description());
                collection.setName(doc.name());
                collection.setApi(api);

                return collection;
            })).get();
        })).get();
    }

    /**
     * 将接口参数定义转为对应的实体
     */
    private List<ApiFoxCollectionParamItem> pasreParamList(String[] define) throws APIException {
        return Arrays.stream(define).map(p -> SplitUtil.split(p, ",")).map(list -> {
            if (list != null && list.size() == 5) return pasreOne(list);
            if (list != null && list.size() == 3) return pasreTwo(list);

            return null;
        }).filter(a -> a != null).collect(Collectors.toList());
    }

    /**
     * 处理请求头(添加默认值)
     */
    private List<ApiFoxCollectionParamItem> pasreHeader(String[] header) throws APIException {
        List<String> defHeader = toLine(StrUtil.defaultIfEmpty(apifoxHeader, ""));

        String[] temp = new String[header.length + defHeader.size()];
        for (int index = 0; index < defHeader.size(); index++) {
            temp[index + header.length] = defHeader.get(index);
        }
        for (int index = 0; index < header.length; index++) {
            temp[index] = header[index];
        }

        return pasreParamList(temp);
    }

    /**
     * 处理请求参数(添加默认值)
     */
    private List<ApiFoxCollectionParamItem> pasreQuery(String[] query) throws APIException {
        List<String> defQuery = toLine(StrUtil.toStringOrEmpty(apifoxQuery));

        String[] temp = new String[query.length + defQuery.size()];
        for (int index = 0; index < defQuery.size(); index++) {
            temp[index + query.length] = defQuery.get(index);
        }
        for (int index = 0; index < query.length; index++) {
            temp[index] = query[index];
        }

        return pasreParamList(temp);
    }

    /**
     * 将接口参数定义转为对应的实体（参数名,示例值,说明）
     */
    private ApiFoxCollectionParamItem pasreTwo(List<String> value) throws APIException {
        ApiFoxCollectionParamItem item = new ApiFoxCollectionParamItem();
        item.setDescription(ConvertUtil.toStr(value.get(2)));
        item.setExample(ConvertUtil.toStr(value.get(1)));
        item.setName(ConvertUtil.toStr(value.get(0)));
        item.setRequired(Boolean.TRUE);
        item.setType("string");

        return item;
    }

    /**
     * 将接口参数定义转为对应的实体（参数名,类型,必填,示例值,说明）
     */
    private ApiFoxCollectionParamItem pasreOne(List<String> value) throws APIException {
        ApiFoxCollectionParamItem item = new ApiFoxCollectionParamItem();
        item.setRequired(ConvertUtil.toBoolean(value.get(2)));
        item.setType(ApifoxType.formatType(value.get(1)));
        item.setDescription(ConvertUtil.toStr(value.get(4)));
        item.setExample(ConvertUtil.toStr(value.get(3)));
        item.setName(ConvertUtil.toStr(value.get(0)));

        return item;
    }

    /**
     * 对应路径进行处理（拼接）
     */
    private static String normalize(String... path) throws APIException {
        return FileUtil.normalize(ArrayUtil.join(path, "/"));
    }

    /**
     * 字符串分割成行列表(移除空行)
     */
    protected static List<String> toLine(String string) {
        return string.lines().filter(line -> {
            return StrUtil.isNotEmpty(line);
        }).collect(Collectors.toList());
    }

    /**
     * 获取请求路径(RequestMapping)
     */
    protected static String[] path(RequestMapping mp) {
        if (mp == null) return new String[]{""};

        if (mp.path().length == 0) {
            return mp.value();
        } else {
            return mp.path();
        }
    }

    /**
     * 获取请求方式(RequestMapping)
     */
    protected static String method(RequestMapping mp) {
        if (mp != null && mp.method().length > 0) {
            return mp.method()[0].name();
        } else {
            return "GET";
        }
    }

    /**
     * 安全的获取上级分组信息
     */
    protected static String group(ApifoxDoc doc) {
        if (doc != null && doc.group() != null) {
            return doc.group();
        }

        return "";
    }

}
